﻿using System;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;

namespace KouakKore
{
    enum KouakShareOption
    {
        NotSet,
        ShareFile,
        ShareDirectory
    }

    enum KouakPathOption
    {
        NotSet,
        RelativePath,
        AbsolutePath
    }

    class KouakServer
    {
        private HttpListener HttpListener = new HttpListener();
        private static AutoResetEvent ListenForNextRequest = new AutoResetEvent(false);
        private string Path;
        private bool File;
        private static bool staticFile;
        private static string staticPath;
        private bool run = true;
        private int Port;

        private static void ListenerCallback(IAsyncResult ar)
        {
            try
            {
                object[] args = ar.AsyncState as object[];

                staticPath = args[1] as string;
                staticFile = (bool)args[2];

                HttpListener hl = args[0] as System.Net.HttpListener;
                if (hl == null) return;

                HttpListenerContext context = hl.EndGetContext(ar);
                ListenForNextRequest.Set();
                if (context == null) return;

                System.Net.HttpListenerRequest request = context.Request;
                if (request.HasEntityBody)
                {
                    using (StreamReader sr = new StreamReader(request.InputStream, request.ContentEncoding))
                    {
                        string requestData = sr.ReadToEnd();
                    }
                }

                using (HttpListenerResponse response = context.Response)
                {
                    try
                    {
                        byte[] b;
                        if (staticFile)
                            b = ResponseContent(staticPath, true);
                        else
                            b = ResponseContent(request.RawUrl, false);

                        if (b != null)
                        {
                            response.ContentLength64 = b.LongLength;
                            response.OutputStream.Write(b, 0, b.Length);
                            b = null; 
                        }
                                              
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
            }
            catch(IOException e)
            {
                Console.WriteLine(e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }


        private static byte[] ResponseContent(string url, bool file)
        {
            byte[] buffer;
            try
            {
                string LocalFileName = "";
                if (file)
                {
                    LocalFileName = staticPath;
                }
                else
                {
                    string path = staticPath;
                    LocalFileName = path + url.Replace('/', '\\');
                }

                if (LocalFileName.EndsWith("\\"))
                {
                    string responseString = GetFilesList(LocalFileName);
                    buffer = Encoding.UTF8.GetBytes(responseString);
                    return buffer;
                }

                using (FileStream WebFile = new FileStream(LocalFileName, FileMode.Open))
                {
                    buffer = new byte[WebFile.Length];
                    WebFile.Read(buffer, 0, buffer.Length);
                    WebFile.Close();

                }
            }
            catch (Exception e)
            {
                string responseString = "ERROR : " + e.Message;
                buffer = Encoding.UTF8.GetBytes(responseString);
            }
            return buffer;
        }

        private static string GetFilesList(string path)
        {
            string result = "<html><head><title>Fileslist</title></head><body>";
            result += "<font size=\"3\" face=\"verdana\">";
            result += "<h1>Navigate</h1>";

            result += "<h2>Dirs</h2>";
            foreach (string s in Directory.GetDirectories(path))
            {
                string[] splited = s.Split('\\');
                string name = splited[splited.Length - 1];
                result += "<a href=\"" + name + "/" + "\">" + name + "</a></br>";
            }
            result += "<h2>Files</h2>";

            foreach (string s in Directory.GetFiles(path))
            {
                string[] splited = s.Split('\\');
                string name = splited[splited.Length - 1];
                result += "<a href=\"" + name + "\">" + name + "</a></br>";
            }
            result += "</font></body></html>";
            return result;
        }

        public KouakServer(string path, KouakShareOption share, KouakPathOption kind, int port)
        {
            if (share == KouakShareOption.ShareDirectory && kind == KouakPathOption.RelativePath)
            {
                this.File = false;
                this.Path = System.Windows.Forms.Application.StartupPath + "\\" + path;
                if (!this.Path.EndsWith("\\")) this.Path += "\\";
            }
            if (share == KouakShareOption.ShareFile && kind == KouakPathOption.RelativePath)
            {
                this.File = true;
                this.Path = System.Windows.Forms.Application.StartupPath + "\\" + path;
            }

            if (share == KouakShareOption.ShareDirectory && kind == KouakPathOption.AbsolutePath)
            {
                this.File = false;
                this.Path = path;
            }
            if (share == KouakShareOption.ShareFile && kind == KouakPathOption.AbsolutePath)
            {
                this.File = true;
                this.Path = path;
            }

            this.Port = port;

            LaunchServer();
        }

        private void LaunchServer()
        {
            Console.WriteLine("Initialize");

            if (HttpListener.Prefixes.Count == 0)
            {
                HttpListener.Prefixes.Add("http://+:" + this.Port.ToString() + "/");
            }
            HttpListener.Start();

            ThreadPool.QueueUserWorkItem(new WaitCallback(Listen));
        }

        public void Stop()
        {
                this.run = false;
        }

        private void Listen(object state)
        {
            Console.WriteLine("Start Listenning");
            try
            {
                do
                {
                    HttpListener.BeginGetContext(new AsyncCallback(ListenerCallback), new object[] { HttpListener, Path, File });
                }
                while (run && ListenForNextRequest.WaitOne());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            Console.WriteLine("Exit");
        }
    }
}
